#!/usr/bin/python
# -*- coding: utf-8 -*-
from __future__ import annotations

import datetime
import threading
from typing import Tuple, List, Dict
import pandas as pd

from core import threadLocal
from core.clock import Clock
from core.dataClasses import TradeInfo, Event
from core.enums import TradeType, EventType, TradeStatus, ThreadName
from event.event_manager import EventManager
from core import logger


class TraderManager:
    """
    交易管理类
    管理交易终端
    """
    def __init__(self):
        pass


class Trader:
    def __init__(self, name: str):
        self.event_manager: EventManager = EventManager.getInstance(threadLocal.get(ThreadName.EVENT_MANAGER.value))
        self.lock: threading.Lock = None

    def setLock(self, lock: threading.Lock):
        self.lock = lock
        return self

    def trade(self, trade_info: TradeInfo) -> bool:
        pass

    def buy(self, tradeInfo: TradeInfo) -> bool:
        pass

    def sell(self, tradeInfo: TradeInfo) -> bool:
        pass

    def testBuy(self, tradeInfo: TradeInfo) -> bool:
        pass

    def testSell(self, tradeInfo: TradeInfo) -> bool:
        pass

    def on_trade(self, e: Event):
        raise NotImplementedError()

    def on_evaluate(self, e: Event):
        raise NotImplementedError()

    def evaluate(self):
        pass


class InventedTrader(Trader):
    """
    虚拟交易
    """
    instances = {
        "default": None
    }

    @classmethod
    def getInstance(cls, name: str = "default") -> InventedTrader:
        if name is None:
            name = "default"
        if name not in cls.instances or cls.instances[name] is None:
            cls.instances[name] = cls(name)
        try:
            threadLocal.get(ThreadName.TRADER.value)
        except:
            threadLocal.add(ThreadName.TRADER.value, name)
        return cls.instances[name]

    def __init__(self, name: str = "default", fund: float = 10000, t: int = 1, rates: float = 0.005):
        """
        :param fund: 资金（元）
        :param t: 交易者买入的股票需要在交易登记日后第t点交易
        :param rates: 手续费费率
        """
        super(InventedTrader, self).__init__(name)
        self.records: pd.DataFrame = pd.DataFrame(data=None, columns=list(TradeInfo().__dict__.keys()))
        self.initialFund: float = fund
        self.currentFund: float = fund
        self.t: int = t
        self.rates: float = rates
        """费率"""

    def setFund(self, fund: float):
        self.initialFund: float = fund
        self.currentFund: float = fund
        return self

    def on_trade(self, e: Event):
        if e.type in [EventType.TRADE_BUY, EventType.TRADE_SELL] and e.data is not None and isinstance(e.data, TradeInfo):
            r = self.trade(e.data)

    def trade(self, tradeInfo: TradeInfo) -> bool:
        if tradeInfo is None:
            return False
        if tradeInfo.tradeType == TradeType.BUY:
            return self.buy(tradeInfo)
        elif tradeInfo.tradeType == TradeType.SELL:
            return self.sell(tradeInfo)
        return False

    def testBuy(self, tradeInfo: TradeInfo) -> bool:
        if tradeInfo.lot * tradeInfo.price * 100 > self.currentFund:
            return False
        return True

    def buy(self, tradeInfo: TradeInfo) -> bool:
        def do():
            if tradeInfo.tradeType == TradeType.BUY:
                if not self.testBuy(tradeInfo):
                    tradeInfo.status = TradeStatus.FAIL
                    tradeInfo.remark = '资金不足'
                    self.records = self.records.append(tradeInfo.__dict__, ignore_index=True)
                    return False
                else:
                    tradeInfo.status = TradeStatus.SUCCESS
                    tradeInfo.setFees(5)
                    tradeInfo.setTotal(tradeInfo.lot * 100 * tradeInfo.price + 5)
                    self.currentFund = self.currentFund - tradeInfo.total
                    self.records = self.records.append(tradeInfo.__dict__, ignore_index=True)
                    return True
            return False
        if self.lock is not None:
            with self.lock:
                return do()
        else:
            return do()

    def testSell(self, tradeInfo: TradeInfo) -> bool:
        if self.records.empty:
            return False
        todayBegin = tradeInfo.time.replace(hour=0, minute=0, second=0)
        histories_buy_todayBefore: pd.DataFrame = self.records.loc[
    (self.records["security"] == tradeInfo.security) & (self.records['status'] == TradeStatus.SUCCESS) &
    (self.records["tradeType"] == TradeType.BUY) & (self.records['time'] < todayBegin) & (self.records["remainLot"] > 0)]
        if histories_buy_todayBefore.empty or histories_buy_todayBefore['remainLot'].sum() < tradeInfo.lot:
            return False
        else:
            return True

    def sell(self, tradeInfo: TradeInfo) -> bool:
        def do():
            if tradeInfo.tradeType == TradeType.SELL:
                if not self.testSell(tradeInfo):
                    tradeInfo.status = TradeStatus.FAIL
                    tradeInfo.remark = "存量不足"
                    self.records = self.records.append(tradeInfo.__dict__, ignore_index=True)
                    return False
                else:
                    todayBegin = tradeInfo.time.replace(hour=0, minute=0, second=0)
                    histories_buy_todayBefore: pd.DataFrame = self.records.loc[
                        (self.records["security"] == tradeInfo.security) & (self.records[
                            'status'] == TradeStatus.SUCCESS) &
                        (self.records["tradeType"] == TradeType.BUY) & (self.records['time'] < todayBegin) &
                        (self.records["remainLot"] > 0)]
                    lot = tradeInfo.lot
                    cost = 0
                    for i in range(histories_buy_todayBefore.shape[0]):
                        if lot == 0:
                            break
                        cur = histories_buy_todayBefore.iloc[i]
                        if cur["remainLot"] <= lot:
                            self.records.loc[(self.records["security"] == cur["security"]) & (self.records['time']==cur["time"]), "remainLot"] = 0
                            lot = lot - cur["remainLot"]
                            cost = cost + cur["remainLot"] * 100 * cur["price"]
                        else:
                            self.records.loc[
                                (self.records["security"] == cur["security"]) & (self.records['time'] == cur["time"]), "remainLot"] = cur["remainLot"] -lot
                            lot = 0
                            cost = cost + lot * 100 * cur["price"]
                    tradeInfo.status = TradeStatus.SUCCESS
                    tradeInfo.setFees(5)
                    tradeInfo.setTotal(tradeInfo.lot * 100 * tradeInfo.price + 5)
                    tradeInfo.setProfit(tradeInfo.total - cost)
                    self.currentFund = self.currentFund + tradeInfo.total
                    self.records = self.records.append(tradeInfo.__dict__, ignore_index=True)
                    return True

            return False
        if self.lock is not None:
            with self.lock:
                return do()
        else:
            return do()

    def on_evaluate(self, e: Event = None):
        ## TODO
        pass
        # self.event_manager.emit(Event(EventType.tradeEvaluate, {}))

    def evaluate(self):
        r = ""
        profit = 0
        profit_time = 0
        lose = 0
        lose_time = 0
        count = 0
        if not self.records.empty:
            for s in set(self.records["security"].tolist()):
                count = count + 1
                df_s: pd.DataFrame = self.records.loc[(self.records["security"] == s) & (self.records["tradeType"] == TradeType.SELL)]
                df_s_1 = df_s[df_s['profit'] >= 0]
                df_s_2 = df_s[df_s['profit'] < 0]
                profit_t = df_s_1['profit'].sum()
                profit_time_t = df_s_1.shape[0]
                lose_t = df_s_2['profit'].sum()
                lose_time_t = df_s_2.shape[0]
                profit = profit + profit_t
                profit_time = profit_time + profit_time_t
                lose = lose + lose_t
                lose_time = lose_time + lose_time_t
                r = r + f"\n\t{s}盈利交易：{profit_time_t}次，盈利金额{profit_t}; 亏钱交易{lose_time_t}次，亏钱{lose_t}元"
        r = f"\n\t总共{count}只股票参与交易，盈利：{profit_time}次， 盈利金额{profit}; 亏钱{lose_time}次，亏钱{lose}元" + r
        return r
